﻿/*
 * This file is part of MonoSettlers.
 *
 * Copyright (C) 2010-2011 Christoph Husse
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Authors: 
 *      # Christoph Husse
 * 
 * Also checkout our homepage: http://opensettlers.codeplex.com/
 */
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using MonoSettlers.RenderSystem;

namespace MonoSettlers
{
    public partial class MainWindow : Form
    {
        private class MouseState
        {
            public Point Pos { get; set; }
            public int Button { get; set; }
        }

        private static Queue<MouseState> m_MouseQueue = new Queue<MouseState>();

        public static IRenderSystem Renderer { get; private set; }
        public static MovableManager PathMgr { get; private set; }
        public static ResourceManager ResMgr { get; private set; }
        public static BuildingManager BuildMgr { get; private set; }
        public static long CurrentCycle { get { return PathMgr.CurrentCycle; } }

        public MainWindow()
        {
            InitializeComponent();
        }

        private void MainWindow_Load(object sender, EventArgs eargs)
        {
            try
            {
                ContainerControl ctrl = new ContainerControl();

                ctrl.Dock = DockStyle.Fill;
                PANEL_Surface.Controls.Add(ctrl);

                PathMgr = new MovableManager(128, 0, (int)CyclePoint.CYCLE_MILLIS);
                Renderer = GLRenderer.CreateInControl(PathMgr.Size, ctrl);
                ResMgr = new ResourceManager(PathMgr);
                BuildMgr = new BuildingManager(PathMgr);

                PathMgr.OnAddMovable += PathMgr_OnAddMovable;
                PathMgr.OnRemoveMovable += PathMgr_OnRemoveMovable;

                ResMgr.OnAddStack += ResMgr_OnAddStack;
                ResMgr.OnRemoveStack += ResMgr_OnRemoveStack;

                AnimationLibrary.OpenFromDirectory(@"..\..\..\Resources\AnimationLibrary");

                for (int x = 0; x < 5; x++)
                {
                    for (int y = 0; y < 5; y++)
                    {
                        PathMgr.AddMovable(new RenderableMovable(CyclePoint.FromGrid(10 + x * 2, 10 + y * 2), false));
                    }
                }

                for (int i = 0; i < 3; i++)
                {
                    ResMgr.DropResource(new Point(5, 5), Resource.Fish, 3);
                    ResMgr.DropResource(new Point(5, 5), Resource.Bread, 3);
                    ResMgr.DropResource(new Point(5, 5), Resource.Coal, 3);
                    ResMgr.DropResource(new Point(5, 5), Resource.Iron, 3);
                    ResMgr.DropResource(new Point(5, 5), Resource.Wine, 3);
                }

                ResMgr.AddResourceStack(CyclePoint.FromGrid(10, 10), ResStackType.Query, Resource.Fish, 8);

                var simThread = new System.Threading.Thread(SimulationLoop);

                simThread.IsBackground = true;
                simThread.Start();

                SetupGUI();

                Renderer.OnMouseDown += Renderer_OnMouseDown;
            }
            catch(Exception e)
            {
                MessageBox.Show(e.ToString());

                Application.Exit();
            }
        }

        static void ResMgr_OnAddStack(ResourceManager inManager, GenericResourceStack inStack)
        {
            inStack.Visual = Renderer.CreateAnimation(inStack.Resource.ToString(), inStack, 1);
            inStack.OnCountChanged += Stack_OnCountChanged;

            Stack_OnCountChanged(inStack, 0, 0);
        }

        static void Stack_OnCountChanged(GenericResourceStack inSender, int inOldValue, int inNewValue)
        {
            var anim = (RenderableAnimation)inSender.Visual;
            int avail = inSender.Available;

            if (avail > 0)
            {
                anim.SetFrameIndex(avail - 1);

                anim.IsVisible = true;
            }
            else
                anim.IsVisible = false;
        }

        static void ResMgr_OnRemoveStack(ResourceManager inManager, GenericResourceStack inStack)
        {

        }

        static void PathMgr_OnRemoveMovable(MovableManager inManager, Movable inMovable)
        {
            RenderableMovable movable = (RenderableMovable)inMovable;

            movable.Visual.PositionTracker = null;
            movable.Visual = null;
        }

        static void PathMgr_OnAddMovable(MovableManager inManager, Movable inMovable)
        {
            RenderableMovable movable = (RenderableMovable)inMovable;

            movable.Visual = Renderer.CreateOrientedAnimation("SettlerIdle", movable, 1);
            movable.OnStop += Movable_OnStop;
        }

        static void Movable_OnStop(IMovable inMovable)
        {
            if (inMovable.Carrying.HasValue)
            {
                ResMgr.DropResource(inMovable.Position.ToPoint(), inMovable.Carrying.Value, 1);

                inMovable.Carrying = null;
            }
        }

        static void Renderer_OnMouseDown(IRenderSystem inSender, int inButton)
        {
            lock (m_MouseQueue)
            {
                // synchronize with simulation thread
                m_MouseQueue.Enqueue(new MouseState()
                {
                    Button = inButton,
                    Pos = inSender.MouseXY,
                });
            }
        }

        static void Synchronized_OnMouseDown(Point inPos, int inButton)
        {
            foreach (var mov in PathMgr.GetMovablesSlow())
            {
                mov.Stop();
            }
            //Point pos = new Point(
            //    (int)(Renderer.MouseXY.X / CyclePoint.CYCLE_MILLIS),
            //    (int)(Renderer.MouseXY.Y / CyclePoint.CYCLE_MILLIS));

            //PathMgr.SetPath(PathMgr.GetMovables(), pos, (success) => { });
        }

        private static void SimulationLoop()
        {
            var watch = new Stopwatch();
            long lastElapsed = 0;
            long cycleMillis = 0;

            watch.Start();

            while (true)
            {
                long elapsed = watch.ElapsedMilliseconds;

                try
                {
                    if (elapsed - lastElapsed < CyclePoint.CYCLE_MILLIS / 3)
                    {
                        System.Threading.Thread.Sleep((int)(CyclePoint.CYCLE_MILLIS / 3));

                        continue;
                    }

                    // handle mouse data
                    MouseState[] mouseData;

                    lock (m_MouseQueue)
                    {
                        mouseData = m_MouseQueue.ToArray();
                        m_MouseQueue.Clear();
                    }

                    foreach (var md in mouseData)
                    {
                        Synchronized_OnMouseDown(md.Pos, md.Button);
                    }

                    for (; cycleMillis < elapsed; cycleMillis += (int)CyclePoint.CYCLE_MILLIS)
                    {
                        PathMgr.AddOneCycle();
                        ResMgr.AddOneCycle();
                    }
                }
                finally
                {
                    lastElapsed = elapsed;
                }
            }
        }

        private void MainWindow_FormClosing(object sender, FormClosingEventArgs e)
        {
            PANEL_Surface.Controls.Clear();
            Renderer.Dispose();
        }
    }
}
